﻿using System;
using System.Collections.Generic;
using System.Text;
using ESRI.ArcGIS.NetworkAnalysis;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Geodatabase;

namespace TrafficAccessibilityIndex
{
    public class ClsPathFinder
    {
        private IGeometricNetwork m_ipGeometricNetwork;
        private IMap m_ipMap;
        private IPointCollection m_ipPoints;
        public IPointToEID m_ipPointToEID;
        private double m_dblPathCost = -1;
        private IEnumNetEID m_ipEnumNetEID_Junctions;
        private IEnumNetEID m_ipEnumNetEID_Edges;
        private IPolyline m_ipPolyline;

        #region Public Function
        //返回和设置当前地图
        public IMap SetOrGetMap
        {
            set { m_ipMap = value; }
            get { return m_ipMap; }
        }
        //打开网络
        public void OpenFeatureDatasetNetwork(IFeatureDataset FeatureDataset)
        {
            CloseWorkspace();
            if (!InitializeNetworkAndMap(FeatureDataset))
                Console.WriteLine("打开出错");
        }
        //输入点的集合
        public IPointCollection StopPoints
        {
            set { m_ipPoints = value; }
            get { return m_ipPoints; }
        }

        //路径成本
        public double PathCost
        {
            get { return m_dblPathCost; }
        }
        //返回路径
        public IPolyline PathPolyLine()
        {
            if (m_ipEnumNetEID_Edges == null)
            {
                return null;
            }
            IEIDInfo ipEIDInfo;
            IGeometry ipGeometry;
            //if (m_ipPolyline != null)
            //{
            //    return m_ipPolyline;
            //}

            m_ipPolyline = new PolylineClass();
            IGeometryCollection ipNewGeometryColl = m_ipPolyline as IGeometryCollection;

            ISpatialReference ipSpatialReference = m_ipMap.SpatialReference;
            IEIDHelper ipEIDHelper = new EIDHelperClass();
            ipEIDHelper.GeometricNetwork = m_ipGeometricNetwork;
            ipEIDHelper.OutputSpatialReference = ipSpatialReference;
            ipEIDHelper.ReturnGeometries = true;

            IEnumEIDInfo ipEnumEIDInfo = ipEIDHelper.CreateEnumEIDInfo(m_ipEnumNetEID_Edges);
            int count = ipEnumEIDInfo.Count;
            ipEnumEIDInfo.Reset();
            for (int i = 0; i < count; i++)
            {
                ipEIDInfo = ipEnumEIDInfo.Next();
                ipGeometry = ipEIDInfo.Geometry;
                ipNewGeometryColl.AddGeometryCollection(ipGeometry as IGeometryCollection);
            }
            return m_ipPolyline;
        }

        /// <summary>
        /// 根据首尾节点返回路径
        /// </summary>
        /// <param name="startEndPts">首尾节点</param>
        /// <returns></returns>
        public IPolyline PathPolyLine(IPoint[] startEndPts)
        {
            if (m_ipEnumNetEID_Edges.Count==0)
            {
                return null;
            }
            IEIDInfo ipEIDInfo;
            IPolyline ipGeometry;

            m_ipPolyline = new PolylineClass();
            IGeometryCollection ipNewGeometryColl = m_ipPolyline as IGeometryCollection;

            ISpatialReference ipSpatialReference = m_ipMap.SpatialReference;
            IEIDHelper ipEIDHelper = new EIDHelperClass();
            ipEIDHelper.GeometricNetwork = m_ipGeometricNetwork;
            ipEIDHelper.OutputSpatialReference = ipSpatialReference;
            ipEIDHelper.ReturnGeometries = true;

            IEnumEIDInfo ipEnumEIDInfo = ipEIDHelper.CreateEnumEIDInfo(m_ipEnumNetEID_Edges);
            int count = ipEnumEIDInfo.Count;
            ipEnumEIDInfo.Reset();
            for (int i = 0; i < count; i++)
            {
                ipEIDInfo = ipEnumEIDInfo.Next();
                bool isSplit; int splitIndex, segIndex; object o = Type.Missing;
                ipGeometry = ipEIDInfo.Geometry as IPolyline;
                if(i==0)
                {
                    ipGeometry.SplitAtPoint(startEndPts[0], false, false, out isSplit, out splitIndex, out segIndex);
                    IPolyline newLine = new PolylineClass();
                    ISegmentCollection lineSegCol = (ISegmentCollection)ipGeometry;
                    ISegmentCollection newSegCol = (ISegmentCollection)newLine;
                    for (int j = segIndex; j < lineSegCol.SegmentCount; j++)
                    {
                        newSegCol.AddSegment(lineSegCol.get_Segment(j), ref o, ref o);
                    }
                    newSegCol.SegmentsChanged();
                    newLine = newSegCol as IPolyline;
                    ipNewGeometryColl.AddGeometryCollection(newLine as IGeometryCollection);
                }
                else if(i==count-1)
                {
                    ipGeometry.SplitAtPoint(startEndPts[1], false, false, out isSplit, out splitIndex, out segIndex);
                    IPolyline newLine = new PolylineClass();
                    ISegmentCollection lineSegCol = (ISegmentCollection)ipGeometry;
                    lineSegCol.RemoveSegments(segIndex, lineSegCol.SegmentCount - segIndex, true);
                    lineSegCol.SegmentsChanged();
                    newLine = lineSegCol as IPolyline;
                    ipNewGeometryColl.AddGeometryCollection(newLine as IGeometryCollection);
                }
                else
                    ipNewGeometryColl.AddGeometryCollection(ipGeometry as IGeometryCollection);
            }
            //bool isSplit;int splitIndex, segIndex;
            //object o = Type.Missing;
            //m_ipPolyline.SplitAtPoint(startEndPts[0], false, false, out isSplit, out splitIndex,out segIndex);

            //IPolyline newLine = new PolylineClass();
            //ISegmentCollection lineSegCol = (ISegmentCollection)m_ipPolyline;
            //ISegmentCollection newSegCol = (ISegmentCollection)newLine;
            //for (int j = segIndex; j < lineSegCol.SegmentCount; j++)
            //{
            //    newSegCol.AddSegment(lineSegCol.get_Segment(j), ref o, ref o);
            //}
            ////lineSegCol.RemoveSegments(segIndex, lineSegCol.SegmentCount - segIndex, true);
            ////lineSegCol.SegmentsChanged();
            ////newSegCol.SegmentsChanged();
            ////IPolyline oldLine = lineSegCol as IPolyline;
            //newLine = newSegCol as IPolyline;
            //newLine.SplitAtPoint(startEndPts[1], false, false, out isSplit, out splitIndex, out segIndex);
            //ISegmentCollection lineSegCol1 = (ISegmentCollection)newLine;
            //lineSegCol1.RemoveSegments(segIndex, lineSegCol1.SegmentCount - segIndex, true);
            //m_ipPolyline = lineSegCol1 as IPolyline;
            return m_ipPolyline;
        }

        /// <summary>
        /// 解决路径
        /// 
        /// </summary>
        /// <param name="WeightName"></param>
        /// <returns>-1表示计算成功，否则返回找不到最近节点的序号</returns>
        public int SolvePath(string WeightName)
        {
            try
            {
                int intEdgeUserClassID;
                int intEdgeUserID;
                int intEdgeUserSubID;
                int intEdgeID = int.MinValue;
                IPoint ipFoundEdgePoint;
                double dblEdgePercent = double.MinValue ;
                /*C#中使用
                 *ITraceFlowSolverGEN替代ITraceFlowSolver
                 */
                ITraceFlowSolverGEN ipTraceFlowSolver = new TraceFlowSolverClass() as ITraceFlowSolverGEN;
                INetSolver ipNetSolver = ipTraceFlowSolver as INetSolver;
                INetwork ipNetwork = m_ipGeometricNetwork.Network;
                ipNetSolver.SourceNetwork = ipNetwork;
                INetElements ipNetElements = ipNetwork as INetElements;
                int intCount = m_ipPoints.PointCount;
                //定义一个边线旗数组
                IEdgeFlag[] pEdgeFlagList = new EdgeFlagClass[intCount];
                for (int i = 0; i < intCount; i++)
                {

                    INetFlag ipNetFlag = new EdgeFlagClass() as INetFlag;
                    IPoint ipEdgePoint = m_ipPoints.get_Point(i);
                    //查找输入点的最近的边线
                    m_ipPointToEID.GetNearestEdge(ipEdgePoint, out intEdgeID, out ipFoundEdgePoint, out dblEdgePercent);

                    if (intEdgeID == int.MinValue || ipFoundEdgePoint == null || dblEdgePercent == double.MinValue)
                    {
                        return i;
                    }

                    ipNetElements.QueryIDs(intEdgeID, esriElementType.esriETEdge, out intEdgeUserClassID, out intEdgeUserID, out intEdgeUserSubID);
                    ipNetFlag.UserClassID = intEdgeUserClassID;
                    ipNetFlag.UserID = intEdgeUserID;
                    ipNetFlag.UserSubID = intEdgeUserSubID;
                    IEdgeFlag pTemp = (IEdgeFlag)(ipNetFlag as IEdgeFlag);
                    pEdgeFlagList[i] = pTemp;
                }
                ipTraceFlowSolver.PutEdgeOrigins(ref pEdgeFlagList);
                INetSchema ipNetSchema = ipNetwork as INetSchema;
                INetWeight ipNetWeight = ipNetSchema.get_WeightByName(WeightName);

                INetSolverWeights ipNetSolverWeights = ipTraceFlowSolver as INetSolverWeights;
                ipNetSolverWeights.FromToEdgeWeight = ipNetWeight;//开始边线的权重
                ipNetSolverWeights.ToFromEdgeWeight = ipNetWeight;//终止边线的权重
                object[] vaRes = new object[intCount - 1];
                //通过findpath得到边线和交汇点的集合
                ipTraceFlowSolver.FindPath(esriFlowMethod.esriFMConnected,
                 esriShortestPathObjFn.esriSPObjFnMinSum,
                 out m_ipEnumNetEID_Junctions, out m_ipEnumNetEID_Edges, intCount - 1, ref vaRes);
                //计算成本
                m_dblPathCost = 0;
                for (int i = 0; i < vaRes.Length; i++)
                {
                    double m_Va = (double)vaRes[i];
                    m_dblPathCost += m_Va;
                }
                m_ipPolyline = null;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

            return -1;
        }

        public int SolvePath(string WeightName, int[] intEdgeIDS, double[] EdgePercent)
        {
            try
            {
                int intEdgeUserClassID;
                int intEdgeUserID;
                int intEdgeUserSubID;
                //int intEdgeID = int.MinValue;
                //IPoint ipFoundEdgePoint;
                //double dblEdgePercent = double.MinValue;
                /*C#中使用
                 *ITraceFlowSolverGEN替代ITraceFlowSolver
                 */
                ITraceFlowSolverGEN ipTraceFlowSolver = new TraceFlowSolverClass() as ITraceFlowSolverGEN;
                INetSolver ipNetSolver = ipTraceFlowSolver as INetSolver;
                INetwork ipNetwork = m_ipGeometricNetwork.Network;
                ipNetSolver.SourceNetwork = ipNetwork;
                INetElements ipNetElements = ipNetwork as INetElements;
                int intCount = m_ipPoints.PointCount;
                //定义一个边线旗数组
                IEdgeFlag[] pEdgeFlagList = new EdgeFlagClass[intCount];
                IPoint[] startEndPt = new IPoint[2];
                for (int i = 0; i < intCount; i++)
                {

                    INetFlag ipNetFlag = new EdgeFlagClass() as INetFlag;
                    //IPoint ipEdgePoint = m_ipPoints.get_Point(i);
                    ////查找输入点的最近的边线
                   
                    //m_ipPointToEID.GetNearestEdge(ipEdgePoint, out intEdgeID, out ipFoundEdgePoint, out dblEdgePercent);
                    //EdgePercent[i] = dblEdgePercent;
                    //if (intEdgeID == int.MinValue || ipFoundEdgePoint == null || dblEdgePercent == double.MinValue)
                    //{
                    //    return i;
                    //}

                    ipNetElements.QueryIDs(intEdgeIDS[i], esriElementType.esriETEdge, out intEdgeUserClassID, out intEdgeUserID, out intEdgeUserSubID);
                    ipNetFlag.UserClassID = intEdgeUserClassID;
                    ipNetFlag.UserID = intEdgeUserID;
                    ipNetFlag.UserSubID = intEdgeUserSubID;
                    IEdgeFlag pTemp = ipNetFlag as IEdgeFlag;;
                    pEdgeFlagList[i] = pTemp;
                }
                ipTraceFlowSolver.PutEdgeOrigins(ref pEdgeFlagList);
                INetSchema ipNetSchema = ipNetwork as INetSchema;
                INetWeight ipNetWeight = ipNetSchema.get_WeightByName(WeightName);

                INetSolverWeights ipNetSolverWeights = ipTraceFlowSolver as INetSolverWeights;
                ipNetSolverWeights.FromToEdgeWeight = ipNetWeight;//开始边线的权重
                ipNetSolverWeights.ToFromEdgeWeight = ipNetWeight;//终止边线的权重
                object[] vaRes = new object[intCount - 1];
                //通过findpath得到边线和交汇点的集合
                ipTraceFlowSolver.FindPath(esriFlowMethod.esriFMConnected,
                 esriShortestPathObjFn.esriSPObjFnMinSum,
                 out m_ipEnumNetEID_Junctions, out m_ipEnumNetEID_Edges, intCount - 1, ref vaRes);
                //计算成本
                m_dblPathCost = 0;

                if (m_ipEnumNetEID_Edges == null)
                {
                    return -1;
                }
                IEIDInfo ipEIDInfo;
                IPolyline ipPolyline;
                m_ipPolyline = new PolylineClass();
                IGeometryCollection ipNewGeometryColl = m_ipPolyline as IGeometryCollection;

                ISpatialReference ipSpatialReference = m_ipMap.SpatialReference;
                IEIDHelper ipEIDHelper = new EIDHelperClass();
                ipEIDHelper.GeometricNetwork = m_ipGeometricNetwork;
                ipEIDHelper.OutputSpatialReference = ipSpatialReference;
                ipEIDHelper.ReturnGeometries = true;

                IEnumEIDInfo ipEnumEIDInfo = ipEIDHelper.CreateEnumEIDInfo(m_ipEnumNetEID_Edges);
                int count = ipEnumEIDInfo.Count;
                ipEnumEIDInfo.Reset();
                for (int i = 0; i < count; i++)
                {

                    ipEIDInfo = ipEnumEIDInfo.Next();
                    ipPolyline = ipEIDInfo.Geometry as IPolyline;


                    if (i == 0)
                    {
                        m_dblPathCost += ipPolyline.Length * (1 - EdgePercent[i]);

                    }
                    else if (i == count - 1)
                    {
                        m_dblPathCost += ipPolyline.Length * EdgePercent[1];
                    }
                    else
                        m_dblPathCost += ipPolyline.Length;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

            return -1;
        }
        #endregion

        #region Private Function
        //初始化
        private bool InitializeNetworkAndMap(IFeatureDataset FeatureDataset)
        {
            IFeatureClassContainer ipFeatureClassContainer;
            IFeatureClass ipFeatureClass;
            IGeoDataset ipGeoDataset;
            ILayer ipLayer;
            IFeatureLayer ipFeatureLayer;
            IEnvelope ipEnvelope, ipMaxEnvelope;
            double dblSearchTol;

            INetworkCollection ipNetworkCollection = FeatureDataset as INetworkCollection;
            int count = ipNetworkCollection.GeometricNetworkCount;
            //获取几何网络工作空间
            m_ipGeometricNetwork = ipNetworkCollection.get_GeometricNetwork(0);
            INetwork ipNetwork = m_ipGeometricNetwork.Network;

            if (m_ipMap != null)
            {
                m_ipMap = new MapClass();
                ipFeatureClassContainer = m_ipGeometricNetwork as IFeatureClassContainer;
                count = ipFeatureClassContainer.ClassCount;
                for (int i = 0; i < count; i++)
                {
                    ipFeatureClass = ipFeatureClassContainer.get_Class(i);
                    ipFeatureLayer = new FeatureLayerClass();
                    ipFeatureLayer.FeatureClass = ipFeatureClass;
                    m_ipMap.AddLayer(ipFeatureLayer);
                }
            }
            count = m_ipMap.LayerCount;
            ipMaxEnvelope = new EnvelopeClass();
            for (int i = 0; i < count; i++)
            {
                ipLayer = m_ipMap.get_Layer(i);
                ipFeatureLayer = ipLayer as IFeatureLayer;
                ipGeoDataset = ipFeatureLayer as IGeoDataset;
                ipEnvelope = ipGeoDataset.Extent;
                ipMaxEnvelope.Union(ipEnvelope);
            }

            m_ipPointToEID = new PointToEIDClass();
            m_ipPointToEID.SourceMap = m_ipMap;
            m_ipPointToEID.GeometricNetwork = m_ipGeometricNetwork;

            double dblWidth = ipMaxEnvelope.Width;
            double dblHeight = ipMaxEnvelope.Height;

            if (dblWidth > dblHeight)
                dblSearchTol = dblWidth / 100;
            else
                dblSearchTol = dblHeight / 100;
            m_ipPointToEID.SnapTolerance = dblSearchTol;

            return true;

        }

        private void CloseWorkspace()
        {
            m_ipGeometricNetwork = null;
            m_ipPoints = null;
            m_ipPointToEID = null;
            m_ipEnumNetEID_Junctions = null;
            m_ipEnumNetEID_Edges = null;
            m_ipPolyline = null;
        }

        #endregion

    }
}

